package gogd

import (
	"errors"
	"image"
	"image/color"
	"image/draw"
	"image/jpeg"
	"image/png"
	"io/ioutil"
	"math"
	"os"

	"github.com/golang/freetype"
	"github.com/nfnt/resize"
)

type Image interface {
	draw.Image
	SubImage(image.Rectangle) image.Image
}

type Color interface {
	color.Color
}

// https://php.net/manual/en/function.imagecreatetruecolor.php
func Imagecreatetruecolor(width, height int) (Image, error) {
	if width <= 0 || height <= 0 {
		return nil, errors.New("width,height must be > 0")
	}
	nrgba := image.NewNRGBA(image.Rect(0, 0, width, height))
	return nrgba, nil
}

// 从文本生成图片
// return image, 新的原点 o_x,o_y
func Imagecreatefromtext(size float64, angle float64, _color Color, fontfile string, text string) (Image, float64, float64, error) {
	angle = angle - math.Floor(angle/360)*360

	fontBytes, e := ioutil.ReadFile(fontfile)
	if e != nil {
		return nil, float64(0), float64(0), e
	}
	font, e := freetype.ParseFont(fontBytes)
	if e != nil {
		return nil, float64(0), float64(0), e
	}

	rect := image.Rectangle{
		Min: image.Point{X: 0, Y: 0},
		Max: image.Point{X: textWidth(text, size), Y: int(1.5 * size)},
	}
	textImage := image.NewNRGBA(rect)

	nctx := freetype.NewContext()
	nctx.SetDPI(72)
	nctx.SetFont(font)
	nctx.SetFontSize(size)
	nctx.SetClip(textImage.Bounds())
	nctx.SetDst(textImage)
	nctx.SetSrc(image.NewUniform(_color))

	pt := freetype.Pt(0, int(size))
	_, e = nctx.DrawString(text, pt)
	if e != nil {
		return nil, float64(0), float64(0), e
	}

	return ImageRotate(textImage, angle, _color)
}

func Imagecreatefrompic(filename string) (Image, error) {
	file, e := os.Open(filename)
	defer file.Close()
	if e != nil {
		return nil, e
	}

	img, _, e := image.Decode(file)
	if e != nil {
		return nil, e
	}

	nrgba := image.NewNRGBA(img.Bounds())

	for y := 0; y < nrgba.Bounds().Dy(); y++ {
		for x := 0; x < nrgba.Bounds().Dx(); x++ {
			nrgba.Set(x, y, img.At(x, y))
		}
	}
	return nrgba, nil
}

// https://php.net/manual/en/function.imagecreatefrompng.php
func Imagecreatefrompng(filename string) (Image, error) {
	return Imagecreatefrompic(filename)
}

// https://php.net/manual/en/function.imagecreatefromjpeg.php
func Imagecreatefromjpeg(filename string) (Image, error) {
	return Imagecreatefrompic(filename)
}

// https://php.net/manual/en/function.imagecolorallocate.php
func Imagecolorallocate(R, G, B, A uint8) Color {
	return &color.NRGBA{R: R, G: G, B: B, A: A}
}

func rotatePoint(x, y, sin, cos float64) (float64, float64) {
	return x*cos + y*sin, y*cos - x*sin
}

// 返回 width,height, offx,offy
func rotatedSize(w, h int, angle float64) (int, int, int, int) {
	angle = angle - math.Floor(angle/360)*360
	if w <= 0 || h <= 0 {
		return 0, 0, 0, 0
	}

	sin, cos := math.Sincos(math.Pi * angle / 180)
	x1, y1 := rotatePoint(float64(w-1), 0, sin, cos)
	x2, y2 := rotatePoint(float64(w-1), float64(h-1), sin, cos)
	x3, y3 := rotatePoint(0, float64(h-1), sin, cos)

	minx := math.Min(x1, math.Min(x2, math.Min(x3, 0)))
	maxx := math.Max(x1, math.Max(x2, math.Max(x3, 0)))
	miny := math.Min(y1, math.Min(y2, math.Min(y3, 0)))
	maxy := math.Max(y1, math.Max(y2, math.Max(y3, 0)))

	dstW := maxx - minx + 1
	if dstW-math.Floor(dstW) > 0.1 {
		dstW++
	}
	dstH := maxy - miny + 1
	if dstH-math.Floor(dstH) > 0.1 {
		dstH++
	}

	return int(dstW), int(dstH), int(minx), int(miny)
}

// 左下角 逆时针 anglee 角度旋转
// 旋转：绕原点逆时针旋转 θ 度角的变换公式是 x'=x*cos + y*sin 与 y'=y*cos - x*sin
func ImageRotate(srcImg Image, angle float64, _color Color) (Image, float64, float64, error) {
	angle = angle - math.Floor(angle/360)*360
	if 0 == angle {
		return srcImg, float64(0), float64(0), nil
	}

	sin, cos := math.Sincos(angle / 180 * math.Pi)
	srcW, srcH := srcImg.Bounds().Dx(), srcImg.Bounds().Dy()
	dstW, dstH, offX, offY := rotatedSize(srcW, srcH, angle)

	//背景涂成透明
	dstImg, _ := Imagecreatetruecolor(int(dstW), int(dstH))
	for i := 0; i < dstW; i++ {
		for j := 0; j < dstH; j++ {
			dstImg.Set(i, j, Imagecolorallocate(0, 0, 0, 0))
		}
	}

	//旋转成新坐标,填色
	dx, dy := float64(0), float64(0)

	//新的原点
	o_x, o_y := rotatePoint(dx, dy, sin, cos)
	o_x -= float64(offX)
	o_y -= float64(offY)

	for x := float64(0); x < float64(srcW); x += 0.5 {
		for y := float64(0); y < float64(srcH); y += 0.5 {
			atcolor := Imagecolorat(srcImg, int(math.Floor(x)), int(math.Floor(y)))

			dx, dy = rotatePoint(x, y, sin, cos)
			dx -= float64(offX)
			dy -= float64(offY)

			dstImg.Set(int(dx), int(dy), atcolor)
		}
	}

	return dstImg, o_x, o_y, nil
}

// https://php.net/manual/en/function.imagecopyresized.php
// srcImg 复制到 dstImg 中
// 从 srcImg src_x, src_y 位置 复制出 src_w, src_h 大小的矩形
// 被缩放成 dst_w, dst_h 然后 贴到 dstImg dst_x, dst_y 位置
func Imagecopyresized(dstImg, srcImg Image, dst_x, dst_y, src_x, src_y, dst_w, dst_h, src_w, src_h int) error {
	src_rectangle := image.Rectangle{
		Min: image.Point{X: src_x, Y: src_y},
		Max: image.Point{X: src_x + src_w, Y: src_y + src_h},
	} //复制范围

	src_subimg := srcImg.SubImage(src_rectangle)                                       //复制出来一块
	resize_img := resize.Resize(uint(dst_w), uint(dst_h), src_subimg, resize.Lanczos3) //缩放后的图片

	dst_rectangle := image.Rectangle{
		Min: image.Point{X: dst_x, Y: dst_y},
		Max: image.Point{X: dst_x + src_w, Y: dst_y + src_h},
	} //贴合范围

	draw.Draw(dstImg, dst_rectangle, resize_img, resize_img.Bounds().Min, draw.Over) //贴合前景图

	return nil
}

// 字符串打印宽度
// ascii<128 1byte,以上2个字节
func textWidth(str string, size float64) int {
	var length = 0
	rune_str := []rune(str)
	for i := 0; i < len(rune_str); i++ {
		if rune_str[i] <= 0xff {
			length++
		} else {
			length += 2
		}
	}

	return length * int(size)
}

// https://php.net/manual/en/function.imagettftext.php
// 在 x,y 位置 写 大小size、颜色color、字体为fontfile的 text
// 逆时针 angle 角度
func Imagettftext(srcImg Image, size float64, angle float64, x, y int, _color Color, fontfile string, text string) error {
	angle = angle - math.Floor(angle/360)*360
	front_image, o_x, o_y, e := Imagecreatefromtext(size, angle, _color, fontfile, text)
	if e != nil {
		return e
	}

	return Imagecopyresized(srcImg, front_image, int(float64(x)-o_x), int(float64(y)-o_y), 0, 0, front_image.Bounds().Dx(), front_image.Bounds().Dy(), front_image.Bounds().Dx(), front_image.Bounds().Dy())
}

// https://php.net/manual/en/function.imagecolorat.php
func Imagecolorat(srcImg Image, x, y int) Color {
	return srcImg.At(x, y)
}

// https://php.net/manual/en/function.imagecolortransparent.php
// 指定的 color 设置成 alpha=0
func Imagecolortransparent(srcImg Image, _color Color) {
	r1, g1, b1, a1 := _color.RGBA()
	r1_uint8 := uint8(r1)
	g1_uint8 := uint8(g1)
	b1_uint8 := uint8(b1)
	a1_uint8 := uint8(a1)

	bs := srcImg.Bounds()
	dx, dy := bs.Dx(), bs.Dy()
	for x := 0; x < dx; x++ {
		for y := 0; y < dy; y++ {
			r2, g2, b2, a2 := srcImg.At(x, y).RGBA()
			r2_uint8 := uint8(r2 >> 8)
			g2_uint8 := uint8(g2 >> 8)
			b2_uint8 := uint8(b2 >> 8)
			a2_uint8 := uint8(a2 >> 8)

			if r1_uint8 == r2_uint8 && g1_uint8 == g2_uint8 && b1_uint8 == b2_uint8 && a1_uint8 == a2_uint8 {
				newColor := color.NRGBA{R: 0, G: 0, B: 0, A: 0}
				srcImg.Set(x, y, newColor)
			}
		}
	}
}

// https://php.net/manual/en/function.imagepng.php
func Imagepng(dstImg Image, filename string) error {
	file, e := os.Create(filename)
	defer file.Close()
	if e != nil {
		return e
	}
	return png.Encode(file, dstImg)
}

// https://php.net/manual/en/function.imagejpeg.php
func Imagejpeg(dstImg Image, filename string, quality int) error {
	file, e := os.Create(filename)
	defer file.Close()
	if e != nil {
		return e
	}
	return jpeg.Encode(file, dstImg, &jpeg.Options{Quality: quality})
}
